package com.example.spring.framework.aop.support;

import com.example.spring.framework.util.StringUtils;
import com.example.spring.framework.aop.aspect.AfterReunrningJAdviceInterceptor;
import com.example.spring.framework.aop.aspect.AfterThrowingJAdviceInterceptor;
import com.example.spring.framework.aop.aspect.MethodBeforeJAdviceInterceptor;
import com.example.spring.framework.aop.config.AopConfig;

import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Classname AdvisedSupport
 * @Date 2019/4/22 23:34
 * @Author admin
 * @Description TODO
 */
public class AdvisedSupport {

    private Class<?> targetClass;

    private Object targetObject;

    private AopConfig aopConfig;

    private Pattern pointCutClassPattern;

    private transient Map<Method, List<Object>> methodCache;

    public AdvisedSupport(AopConfig aopConfig) {
        this.aopConfig = aopConfig;
    }

    public boolean pointCutMatch() {
        if (this.targetClass.toString().indexOf("transfer") != -1){
            System.out.println(">>>>> 222222");
        }
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
        List<Object> cached = methodCache.get(method);
        if (null == cached) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            cached = methodCache.get(m);
            this.methodCache.put(m, cached);
        }
        return cached;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    public void parse() {
        String pointCut = aopConfig.getPointCut();

        // 要增强的类 class com.example.ioc.service.impl.TransferServiceImpl
        pointCutClassPattern = Pattern.compile(pointCut);

        try {
            methodCache = new HashMap<>();
            Pattern pattern = Pattern.compile(pointCut);

            Class aspectClass = Class.forName(this.aopConfig.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<String, Method>();
            for (Method m : aspectClass.getMethods()
            ) {
                aspectMethods.put(m.getName(), m);
            }

            for (Method m : this.targetClass.getMethods()) {
                String methodString = m.toString();
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.indexOf("throws")).trim();
                }
                if (methodString.indexOf("transfer") != -1){
                    System.out.println(">>>>> 11111");
                }

                Matcher matcher = pattern.matcher(methodString);
                if (matcher.matches()) {
                    //执行器链
                    //把每一个方法包装成MethodIterceptor
                    List<Object> advices = new LinkedList<Object>();

                    //before
                    if (StringUtils.isNotEmpty(aopConfig.getAspectBefore())) {
                        advices.add(new MethodBeforeJAdviceInterceptor(aspectMethods.get(aopConfig.getAspectBefore()), aspectClass.newInstance()));
                    }
                    //after
                    if (StringUtils.isNotEmpty(aopConfig.getAspectAfter())) {
                        advices.add(new AfterReunrningJAdviceInterceptor(aspectMethods.get(aopConfig.getAspectAfter()), aspectClass.newInstance()));
                    }
                    //after throwing
                    if (StringUtils.isNotEmpty(aopConfig.getAspectAfterThrow())) {
                        AfterThrowingJAdviceInterceptor afterThrowingAdviceInterceptor = new AfterThrowingJAdviceInterceptor(aspectMethods.get(aopConfig.getAspectAfterThrow()), aspectClass.newInstance());
                        afterThrowingAdviceInterceptor.setThrowingName(aopConfig.getAspectAfterThrowingName());
                        advices.add(afterThrowingAdviceInterceptor);
                    }

                    methodCache.put(m, advices);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getTargetObject() {
        return targetObject;
    }

    public void setTargetObject(Object targetObject) {
        this.targetObject = targetObject;
    }
}
